﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Ferpect.ComponentModel
{
    [GameService(typeof(IViewportService))]
    public class ViewportService : ControllerComponent, IViewportService
    {
        private Rectangle viewportArea;
        private Rectangle titleSafeArea;
        private Vector2[] viewAnchors;
        private Vector2[] safeAnchors;

        public ViewportService()
        {
            this.UpdateOrder = Int32.MinValue;
        }

        protected void UpdateAnchorPositions()
        {
            this.UpdateAnchorPositions(this.viewAnchors, false);
            this.UpdateAnchorPositions(this.safeAnchors, true);
        }

        private void UpdateAnchorPositions(Vector2[] positions, bool titleSafe)
        {
            if (positions == null) throw new ArgumentNullException("positions");
            if (positions.Length != 9) throw new ArgumentException("Array does not match expected length of 9.", "positions");

            Rectangle area = titleSafe ? this.titleSafeArea : this.viewportArea;

            positions[ToIndex(RelativeLocation.TopLeft)] = ViewportService.ComputeAnchorPosition(RelativeLocation.TopLeft, area);
            positions[ToIndex(RelativeLocation.TopCenter)] = ViewportService.ComputeAnchorPosition(RelativeLocation.TopCenter, area);
            positions[ToIndex(RelativeLocation.TopRight)] = ViewportService.ComputeAnchorPosition(RelativeLocation.TopRight, area);
            positions[ToIndex(RelativeLocation.MiddleLeft)] = ViewportService.ComputeAnchorPosition(RelativeLocation.MiddleLeft, area);
            positions[ToIndex(RelativeLocation.MiddleCenter)] = ViewportService.ComputeAnchorPosition(RelativeLocation.MiddleCenter, area);
            positions[ToIndex(RelativeLocation.MiddleRight)] = ViewportService.ComputeAnchorPosition(RelativeLocation.MiddleRight, area);
            positions[ToIndex(RelativeLocation.BottomLeft)] = ViewportService.ComputeAnchorPosition(RelativeLocation.BottomLeft, area);
            positions[ToIndex(RelativeLocation.BottomCenter)] = ViewportService.ComputeAnchorPosition(RelativeLocation.BottomCenter, area);
            positions[ToIndex(RelativeLocation.BottomRight)] = ViewportService.ComputeAnchorPosition(RelativeLocation.BottomRight, area);
        }

        private static int ToIndex(RelativeLocation anchor)
        {
            switch (anchor)
            {
                case RelativeLocation.TopLeft:
                    return 0;
                case RelativeLocation.TopCenter:
                    return 1;
                case RelativeLocation.TopRight:
                    return 2;
                case RelativeLocation.MiddleLeft:
                    return 3;
                case RelativeLocation.MiddleCenter:
                    return 4;
                case RelativeLocation.MiddleRight:
                    return 5;
                case RelativeLocation.BottomLeft:
                    return 6;
                case RelativeLocation.BottomCenter:
                    return 7;
                case RelativeLocation.BottomRight:
                    return 8;
            }
            throw new ArgumentOutOfRangeException("anchor");
        }

        public static Vector2 ComputeAnchorPosition(RelativeLocation anchor, Rectangle area)
        {
            Vector2 position = new Vector2();

            switch (anchor)
            {
                case RelativeLocation.TopLeft:
                case RelativeLocation.TopCenter:
                case RelativeLocation.TopRight:
                    position.Y = area.Y;
                    break;
                case RelativeLocation.MiddleLeft:
                case RelativeLocation.MiddleCenter:
                case RelativeLocation.MiddleRight:
                    position.Y = area.Y + (area.Height / 2);
                    break;
                case RelativeLocation.BottomLeft:
                case RelativeLocation.BottomCenter:
                case RelativeLocation.BottomRight:
                    position.Y = area.Y + area.Height;
                    break;
            }
            switch (anchor)
            {
                case RelativeLocation.TopLeft:
                case RelativeLocation.MiddleLeft:
                case RelativeLocation.BottomLeft:
                    position.X = area.X;
                    break;
                case RelativeLocation.TopCenter:
                case RelativeLocation.MiddleCenter:
                case RelativeLocation.BottomCenter:
                    position.X = area.X + (area.Width / 2);
                    break;
                case RelativeLocation.TopRight:
                case RelativeLocation.BottomRight:
                case RelativeLocation.MiddleRight:
                    position.X = area.X + area.Width;
                    break;
            }
            return position;
        }

        #region IGameComponent Members

        public override void Initialize()
        {
            base.Initialize();

            Viewport viewport = this.GraphicsDevice.Viewport;

            this.viewportArea = new Rectangle(viewport.X, viewport.Y, viewport.Width, viewport.Height);
            this.titleSafeArea = viewport.TitleSafeArea;

            this.viewAnchors = new Vector2[9];
            this.safeAnchors = new Vector2[9];

            this.UpdateAnchorPositions();
        }

        #endregion

        #region IUpdateable Members

        public override void Update(GameTime gameTime)
        {
            Viewport viewport = this.GraphicsDevice.Viewport;

            this.viewportArea = new Rectangle(viewport.X, viewport.Y, viewport.Width, viewport.Height);
            this.titleSafeArea = viewport.TitleSafeArea;

            this.UpdateAnchorPositions();
        }

        #endregion

        #region IViewportService Members

        public Rectangle ViewportArea
        {
            get { return this.viewportArea; }
            protected set { this.viewportArea = value; }
        }

        public Rectangle TitleSafeArea
        {
            get { return this.titleSafeArea; }
            protected set { this.titleSafeArea = value; }
        }

        public Vector2 GetAnchorPosition(RelativeLocation anchor)
        {
            return this.GetAnchorPosition(anchor, false);
        }

        public Vector2 GetAnchorPosition(RelativeLocation anchor, bool titleSafe)
        {
            int index = ToIndex(anchor);
            return titleSafe ? this.safeAnchors[index] : this.viewAnchors[index];
        }

        #endregion
    }
}
